import numpy as np

from classfile.class_file import ClassFile
from heap.access_flag import AccessFlagHelper
from heap.primitive import PRIMITIVE_TYPES
from heap.slot import Slots


class HeapObject:
    def __init__(self):
        self.klass: Klass = None  # 特定的指向 java.lang.Class的元数据 即指向 java.lang.Class.Klass
        self.data = None  # 存储在内存中的数据
        self.extra = None  # 指向真实类的元数据  xxx.xxx.Test2.Klass

    def set_class(self, klass):
        self.klass = klass

    def set_data(self, data):
        self.data = data

    def set_extra(self, extra):
        self.extra = extra

    def get_class(self):
        return self.klass

    def get_data(self):
        return self.data

    def get_extra(self):
        return self.extra

    # object 是否是 klass 的实例
    def isInstanceOf(self, klass):
        return self.klass.isAssignableFrom(klass)

    def getFields(self) -> Slots:
        """
        返回存储在self.data中的字段数据。

        Returns:
        Slots: 存储在self.data中的字段数据。
        """
        data: Slots = self.data
        return data

    def setRefVar(self, name, descriptor, ref):
        field = self.klass.getField(name, descriptor, False)
        slots = self.getFields()
        slots.setRef(field.slotId, ref)

    def refs(self):
        return self.data

    def bytes(self):
        return self.data

    def Fields(self) -> Slots:
        return self.data


class Klass:
    def __init__(self):
        # 访问控制符号
        self.accessFlags: np.uint16 = None
        # klass 类名
        self.name: str = None
        # klass 源文件名称
        self.sourceFile: str = None
        # klass 父类名称
        self.superClassName: str = None
        # 接口名称 字符串数组
        self.interfaceNames = []
        # 常量池
        self.constantPool = None
        # 字段列表
        self.fields = []
        # 方法列表
        self.methods = []
        # 类加载器
        self.classLoader = None
        # 父类
        self.superClass: Klass = None
        # 接口列表
        self.interfaces = []
        # 堆空间的字节码对象 反射使用
        self.heapClass: HeapObject = None
        # 成员变量的槽数
        self.instanceSlotCount: int = None
        # 静态变量的槽数
        self.staticSlotCount: int = None
        # 静态变量
        self.staticVars: Slots = None
        # 初始化标识
        self.initFlag = False

    def newHeapClass(self) -> HeapObject:
        hp = HeapObject()
        hp.klass = self
        hp.data = Slots().makeSlot(self.instanceSlotCount)
        return hp

    def getHeapClass(self) -> HeapObject:
        return self.heapClass

    def setAccessFlags(self, cf: ClassFile):
        self.accessFlags = cf.accessFlags

    # self 是否是 klass 的子类或者实现了klass的接口
    def isAssignableFrom(self, klass):
        if self == klass:
            return True
        if self.isArray():
            if klass.isArray():
                sc = self.componentClass()
                tc = klass.componentClass()
                return sc == tc or sc.isAssignableFrom(tc)
            else:
                if klass.isInterface():
                    return klass.isJavaLangCloneable() or klass.isJavaIoSerializable()
                else:
                    return klass.isJavaLangObject()
        else:
            if self.isInterface():
                if klass.isInterface():
                    return klass.isSuperInterfaceOf(self)
                else:
                    return False

    def isAccessibleTo(self, klass):
        if self.isPublic():
            return True
        if (not self.isPrivate()) and self.getPackageName() == klass.getPackageName():
            return True
        if self.isPrivate() and self.name == klass.name:
            return True
        return False

    def getField(self, name, descriptor, isStatic: bool):
        c = self
        while True:
            for field in c.fields:
                if field.name == name and field.descriptor == descriptor and field.isStatic() == isStatic:
                    return field
            if c.superClass is None:
                return None
            c = c.superClass

    def findField(self, name, descriptor, isStatic: bool):
        f = self.findLocalField(name, descriptor, isStatic)
        if f is not None:
            return f
        f = self.findSuperClassFeild(name, descriptor, isStatic)
        if f is not None:
            return f
        return self.findInterfaceFeild(name, descriptor, isStatic)

    def findLocalField(self, name, descriptor, isStatic: bool):
        for field in self.fields:
            if field.name == name and field.descriptor == descriptor and field.isStatic() == isStatic:
                return field

    def findSuperClassFeild(self, fieldKlass, name, descriptor, isStatic: bool):
        if self.superClass is None:
            return None
        c = self.superClass
        while True:
            f = c.findLocalField(fieldKlass, name, descriptor, isStatic)
            if f is not None:
                return f
            c = c.superClass
            if c is None:
                return None

    def findInterfaceFeild(self, fieldKlass, name, descriptor, isStatic: bool):
        if self.interfaces is None:
            return None
        for interface in self.interfaces:
            f = interface.findLocalField(fieldKlass, name, descriptor, isStatic)
            if f is not None:
                return f
        return None

    def getMethod(self, name, descriptor, isStatic: bool):
        c = self
        while True:
            for method in c.methods:
                if method.name == name and method.descriptor == descriptor and method.isStatic() == isStatic:
                    return method
            if c.superClass is None:
                return None
            c = c.superClass

    def isSubClassOf(self, father) -> bool:
        sc = self.superClass
        while True:
            if sc is None:
                return False
            if sc == father:
                return True
            sc = sc.superClass

    def getPackageName(self) -> str:
        packageNameIndex = self.name.lastIndexOf('/')
        if packageNameIndex > 0:
            return self.name[:packageNameIndex]
        else:
            return ""

    def isPublic(self):
        return AccessFlagHelper.isPublic(accessFlag=int(self.accessFlags))

    def isInterface(self):
        return AccessFlagHelper.isInterface(accessFlag=int(self.accessFlags))

    def isAbstract(self):
        return AccessFlagHelper.isAbstract(accessFlag=int(self.accessFlags))

    def isFinal(self):
        return AccessFlagHelper.isFinal(accessFlag=int(self.accessFlags))

    def isSuper(self):
        return AccessFlagHelper.isSuper(accessFlag=int(self.accessFlags))

    def isSynthetic(self):
        return AccessFlagHelper.isSynthetic(accessFlag=int(self.accessFlags))

    def isAnnotation(self):
        return AccessFlagHelper.isAnnotation(accessFlag=int(self.accessFlags))

    def isEnum(self):
        return AccessFlagHelper.isEnum(accessFlag=int(self.accessFlags))

    def isArray(self):
        return self.name[0] == '['

    def componentClass(self):
        pass

    def getMainMethod(self):
        return self.getMethod("main", "([Ljava/lang/String;)V", True)

    def arrayClass(self):
        arrayClassName = "[" + self.toDescriptor(self.name)
        return self.classLoader.loadClass(arrayClassName)

    def toDescriptor(self, className):
        if className[0] == '[':
            return className
        else:
            for primitiveType in PRIMITIVE_TYPES:
                if className == primitiveType.value:
                    return primitiveType.sign

            return "L" + className + ";"

    def newArray(self, length) -> HeapObject:
        if not self.isArray():
            raise Exception("java.lang.IllegalArgumentException: " + self.name + " is not an array class")
        hp = HeapObject()
        hp.klass = self
        hp.data = []
        for i in range(0, length):
            hp.data.append(None)
        hp.extra = None
        return hp

    def startInit(self):
        self.initFlag = True

    def isInit(self):
        return self.initFlag

    def getClinitMethod(self):
        return self.getMethod("<clinit>", "()V", True)

    def getConstantPool(self):
        return self.constantPool

    def getClassLoader(self):
        return self.classLoader

    def getName(self):
        return self.name

    def getStaicFields(self):
        return self.staticVars

    def findInterfaceMethod(self, name, descriptor):
        method = self.findLocalMethod(name, descriptor, False)
        if method is not None:  # 先在本类中查找
            return method
        if len(self.interfaces) > 0:
            for interface in self.interfaces:
                method = interface.findInterfaceMethod(name, descriptor)
                if method is not None:
                    return method
        return None

    def lookMethodInInterface(self, name, descriptor):
        for interface in self.interfaces:
            method = interface.lookLocalMethod(name, descriptor, False)
            if method is not None:
                return method
        return None

    def lookLocalMethod(self, name, descriptor, isStatic: bool):
        for method in self.methods:
            if method.name == name and method.descriptor == descriptor and method.isStatic() == isStatic:
                return method

    def lookMethodInSuperClass(self, name, descriptor, isStatic: bool):
        if self.superClass is None:
            return None
        c = self.superClass
        while True:
            method = c.findLocalMethod(name, descriptor, isStatic)
            if method is not None:
                return method
            c = c.superClass
            if c is None:
                return None

    def lookMethod(self, name, descriptor):
        method = self.lookLocalMethod(name, descriptor, False)
        if method is not None:
            return method
        method = self.lookMethodInSuperClass(name, descriptor, False)
        if method is not None:
            return method
        return self.lookMethodInInterface(name, descriptor)
